/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_ACCEPTOR__HEADER_FILE__
#define __COID_SERVER_ACCEPTOR__HEADER_FILE__

#include "coid/comm/net.h"
//#include "coid/comm/tree/nodeclass.h"
#include "coid/comm/pthreadx.h"
#include "serverface.h"
#include "connection.h"
#include "treemgr.h"

namespace coid {

////////////////////////////////////////////////////////////////////////////////
struct AcceptorCoid : CoidNode
{
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(AcceptorCoid,"0.0",CoidNode);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

    virtual token short_name() const        { static token _T("acceptor"); return _T; }

    virtual opcd accept_connections() = 0;

    bool spawn();
    static void* thread_fnc(void* p);

    charstr& get_server_address( charstr& buf ) const
    {
        _accp_addr.getHost(buf, true);
        return buf;
    }

    netAddress* get_server_address( netAddress* buf ) const {
        *buf = _accp_addr;
        return buf;
    }

    netaddr& get_server_address( netaddr& buf ) const
    {
        NetAddress2netaddr( &_accp_addr, &buf );
        return buf;
    }

    ushort get_server_port() const
    {
        return _accp_addr.getPort();
    }

    opcd find_service( const token& name, const version& svcver, NODEREF<ServiceCoid>& psvc );
    opcd find_instance( uint soid, const token& name, const version& svcver, NODEREF<ServiceInstanceCoid>& psvc );

    opcd attach_svci( uint& connflags, ServiceCoid* svc, NODEREF<ServiceInstanceCoid>& svci );
    opcd reattach_svci( uint& connflags, NODEREF<ServiceInstanceCoid>& svci );


    AcceptorCoid() : _semaphore(NOINIT)
    {
        _mutex.set_name( "AcceptorCoid" );
    }

    opcd init( int port, const charstr& name, bool reliable = true )
    {
        _nwaiting = 0;
        RASSERTX( _semaphore.init(1), "error initializing semaphore" );

        _acceptor.open( reliable );
        if( _acceptor.bind( "", port ) )
        {
            post_error(0) << "can't bind to port " << port << " errno: " << errno << BINSTREAM_FLUSH;
            return ersFAILED;
        }

        _thrname = name;
        _thrname << " listener";

        //_acceptor.getAddress( &_accp_addr );
        netAddress::getLocalHost( &_accp_addr );
        _accp_addr.setPort( port );

        if(reliable)
            _acceptor.listen( 0 );

        charstr buf;
        post_info(0) << "starting listener on port " << _accp_addr.getPort()
            << " (" << _accp_addr.getHostName(buf, true) << ")" << EOM;

        object_name(name);
        return 0;
    }

    virtual void on_exit_signalled();


    static void init_pid( uint pid ) { _pid = pid; }

    virtual ~AcceptorCoid()
    {
        _acceptor.close();
    }

protected:

    charstr     _thrname;

    netAddress  _accp_addr;
    netSocket   _acceptor;

    thread_semaphore    _semaphore;
    uint    _nwaiting;
    static uint _pid;                       ///< our process id
};

////////////////////////////////////////////////////////////////////////////////
const int COID_SERVER_PORT = 7777;

struct CoidAcceptorCoid : public AcceptorCoid
{
    uchar _conchar;

    struct header
    {
        version coidver;
        charstr nick;
        uint    flags;
        CreateInterfaceInfo cinfo;
        charstr service;
        uint    soid;
        uint    pid;
        void*   ithis;
        
        netAddress addr;
        uint    mode;

        void set_access_flags( uint mask, bool samemachine, bool sameprocess )
        {
            flags &= mask;

            if( !samemachine )
                flags &= ~(uint)(StdProtocolMethod::ConnectFlags::fACCESS_MODE_DIRECT | StdProtocolMethod::ConnectFlags::fACCESS_MODE_INTERPROCESS);
            else if( !sameprocess )
                flags &= ~(uint)StdProtocolMethod::ConnectFlags::fACCESS_MODE_DIRECT;
        }
        
        uint get_mode()
        {
            uint n;
            if( flags & StdProtocolMethod::ConnectFlags::fACCESS_MODE_DIRECT )
                n = 0;
            else if( flags & StdProtocolMethod::ConnectFlags::fACCESS_MODE_INTERPROCESS )
                n = 1;
            else if( flags & StdProtocolMethod::ConnectFlags::fACCESS_MODE_REMOTE )
                n = 2;
            else
                n = UMAX32;

            flags &= (~(uint)StdProtocolMethod::ConnectFlags::xACCESS_MODE) | (StdProtocolMethod::ConnectFlags::fACCESS_MODE_DIRECT << n);
            return mode = n;
        }

        friend binstream& operator >> ( binstream& bin, header& h )
        {
            h.mode = UMAX32;

            bin >> h.coidver >> h.nick >> h.flags >> h.cinfo._clientver >> h.service >> h.cinfo._nmethods;
            bin.read_ptr(h.cinfo._methflags);
            bin >> h.soid >> h.pid;
            bin.read_ptr(h.ithis);
            return bin;
        }
    };


    opcd check_server_version( header& hdr ) const;
    opcd attach_service_inst( binstream& bin, header& hdr, NODEREF<ServiceInstanceCoid>& svci );
    opcd attach_service_inst( uint oid, token name_ver, binstream& bin, metastream* meta, NODEREF<ServiceInstanceCoid>& svci );


    opcd attach_new_connection( local<netstream>& bin, const netAddress& addr );
    opcd attach_to_connection( NODEREF<ConnectionCoid>& con, binstream& bin, const netAddress& addr );
    opcd attach_service_remote( binstream& bin, const netAddress& addr, uint* svciid );
    opcd attach_service_remote( uint oid, token name_ver, binstream& bin, metastream* meta, uint* svciid );


    virtual token short_name() const        { static token _T("accpcoid"); return _T; }


    virtual ~CoidAcceptorCoid() { }
    CoidAcceptorCoid()
    {
        _conchar = StdProtocolMethod::CONNECT_CHAR;
        _mutex.set_name( "CoidAcceptorCoid" );
    }

    virtual opcd accept_connections();
    //opcd attach_connection( ConnectionCoid* pcon, netstream& pipe, const netAddress& addr );


    opcd init( int port = COID_SERVER_PORT )
    {
        return AcceptorCoid::init( port, "coid acceptor" );
    }
    
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(CoidAcceptorCoid,"0.0",AcceptorCoid);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;
};


////////////////////////////////////////////////////////////////////////////////
struct UniAcceptorCoid : public AcceptorCoid
{

    virtual token short_name() const        { static token _T("accpuni"); return _T; }
    
    virtual ~UniAcceptorCoid() { }
    UniAcceptorCoid()
    {
        _mutex.set_name( "UniAcceptorCoid" );
    }

    virtual opcd accept_connections();
    opcd attach_connection( netSocket& pipe, const netAddress& addr );

    opcd init( int port, NODEREF<ServiceCoid>& service );

    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(UniAcceptorCoid,"0.0",AcceptorCoid);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

protected:
    ServiceCoid* _svc;
};

////////////////////////////////////////////////////////////////////////////////
struct UniBoundAcceptorCoid : public UniAcceptorCoid
{
    virtual token short_name() const        { static token _T("accpunib"); return _T; }

    virtual ~UniBoundAcceptorCoid() { }
    UniBoundAcceptorCoid()
    {
        _mutex.set_name( "UniBoundAcceptorCoid" );
    }

    virtual opcd accept_connections();
    opcd process_connection( ServiceInstanceCoid* svci, netSocket& pipe, const netAddress& addr );

    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(UniBoundAcceptorCoid,"0.0",UniAcceptorCoid);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;
};

} // namespace coid

#endif //__COID_SERVER_ACCEPTOR__HEADER_FILE__

